package com.wngbms.async;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

@Slf4j
public class CompletionServiceDemo {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        ExecutorCompletionService<Integer> service = new ExecutorCompletionService<>(executorService);

//        m1(executorService);
//        m2(service);

        List<Future<Integer>> futures = new ArrayList<>();

        Future<Integer> submit = service.submit(() -> {
            log.info("submit");
            TimeUnit.SECONDS.sleep(3);
            log.info("submit");
            return 324;
        });
        futures.add(submit);

        Future<Integer> submit1 = service.submit(() -> {
            log.info("submit1");
            TimeUnit.SECONDS.sleep(2);
            log.info("submit1");
            return 66;
        });
        futures.add(submit1);

        Future<Integer> submit2 = service.submit(() -> {
            log.info("submit2");
            TimeUnit.SECONDS.sleep(6);
            log.info("submit2");
            return 88;
        });
        futures.add(submit2);

        try {
            Integer integer = service.take().get();
            log.info("value: {}", integer);
        } finally {
            for (Future<Integer> future : futures) {
                future.cancel(true);
            }
        }

        // java.util.concurrent.CancellationException
//        Integer integer = service.take().get();
//        log.info("value: {}", integer);

    }

    private static void m2(ExecutorCompletionService<Integer> service) throws InterruptedException, ExecutionException {
        Future<Integer> submit3 = service.submit(() -> {
            log.info("submit3");
            TimeUnit.SECONDS.sleep(5);
            log.info("submit3 end");
            return 18;
        });
        log.info("obj:{}", submit3);

        Future<Integer> submit4 = service.submit(() -> {
            log.info("submit4");
            try {
                TimeUnit.SECONDS.sleep(7);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("submit4 end");
        }, 88);
        log.info("obj:{}", submit4);

        for (int i = 0; i < 2; i++) {
            Future<Integer> take = service.take();
            log.info("take:{}", take);
            log.info("val:{}", take.get());
        }
    }

    private static void m1(ExecutorService executorService) throws InterruptedException {
        Future<?> submit = executorService.submit(() -> {
            log.info("submit");
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("submit end");
        });
        Future<Object> submit1 = executorService.submit(() -> {
            log.info("submit1");
            TimeUnit.SECONDS.sleep(3);
            log.info("submit1 end");
            return 17;
        });
        Future<Object> submit2 = executorService.submit(() -> {
            log.info("submit2");
            try {
                TimeUnit.SECONDS.sleep(7);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("submit2 end");
        }, null);

        BlockingQueue<Object> bq = new LinkedBlockingQueue<>();
        executorService.submit(() -> {
            try {
                bq.put(submit.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });
        executorService.submit(() -> {
            try {
                bq.put(submit1.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });
        executorService.submit(() -> {
            try {
                bq.put(submit2.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

        for (int i = 0; i < 3; i++) {
            Object take = bq.take();
            log.info("value:{}", take);
        }
    }

}
